package cn.wps.moffice.main.local.filebrowser.operator.fscache;

import java.io.File;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.Set;
import java.util.Stack;

import android.media.MediaScannerConnection;
import android.util.SparseArray;
import cn.wps.moffice.OfficeApp;
import cn.wps.moffice.common.insertpic.InsertPicDataSource;
import cn.wps.moffice.main.local.filebrowser.BrowserConst;
//import cn.wps.moffice.main.local.filebrowser.operator.FileBrowserOperatorCore;
import cn.wps.moffice.main.local.filebrowser.operator.seek.BWList;
import cn.wps.moffice.util.StringUtil;

/**
 * 本地目录 和 所有文档 抽象缓存.
 * 
 * 为 上层提供 文件系统的 搜素服务及其缓存数据, 内部使用 '数据中心' 进行数据的管理.
 * 
 * 一. 数据服务 [代理 DataCenter]
 * 1. getAllResultSets() 提供深度搜素结果缓存列表, 按文件列表的索引进行存放.
 * 2. getAllSearchSets() 提供内存搜素缓存列表,    按文件列表的索引进行存放.
 * 
 * 二. 搜索服务
 * 1. onScan()           提供 [首次登录 扫描] 服务.
 * 2. onFresh()          提供 [下拉刷新] 服务.
 * 3. onSeek()           提供 [搜索] 服务.
 * 4. onSeekCurrent()    提供 [搜素-当前目录及子目录] 服务.
 * 
 * 三. SOURCE
 * 1. mAllDocSource      所有文档搜索: 内部及外部存储卡.
 * 2. mBrowserSource     本地目录制定的当前目录.
 * 3. defaultSource      默认为 sdcard 。
 * @author wangxiaohua
 *
 */
public abstract class AbstractFileScanner implements IFileScanner
{
	private SparseArray<Set<String>> regexs = null;
	private Set<String> pictureTypes = null;
	
	protected static IFileScanner scanner = null;
	
	//=========public====
	
	// 本地目录 搜素当前目录.
	public void onSeekCurrent(String path, boolean seekPicture, boolean seekAll)
	{
		//本地目录搜索，也要检查一下被删除的文件
		DataCenter.getInstance().deleteUnExistScan();
		
		if (seekAll)
		{
			seekAll(true, seekPicture);
		}
		else
		{
			seekCurrentFolder(new File(path), true, seekPicture);
		}
	}
	
	// 对应下拉刷新.
	public void onFresh()
	{
		if (!DataCenter.getInstance().loadPersistents()) //没有读取过持久化
		{
			seelAllDir();
		}
		else //读取过只做同步就行
		{
			HashMap<String, Long> dirMap = DataCenter.getInstance().getCacheDirPool();
			syncAllChangedFiles(dirMap);//同步
			
			// 读取过，但是可能新插入了卡, 新插入的要重新扫描
			Stack<File> allCard = getSource();
			for (File folder : allCard) {
				if (dirMap.get(folder.getAbsolutePath()) == null) {
					seekCurrentFolder(folder, true, false);
				}
			}
		}
		DataCenter.getInstance().savePersistents();
		return;
	}
	
	//=========end=====
	
	//====pirvate=======	
	protected AbstractFileScanner() 
    { 
        init();
    }
    
	private void init() 
    {
		getAllRegex();
		getPictureTypes();
	}
		
	private Set<String> getPictureTypes()
	{
		if (null == pictureTypes)
		{
			pictureTypes = new HashSet<String>();
			pictureTypes.addAll(Arrays.asList(InsertPicDataSource.filterType));
		}
		
		return pictureTypes;
	}
	
	private SparseArray<Set<String>> getAllRegex()
	{
		if (null == regexs)
		{
			regexs = BrowserConst.getRegexs();
		}
		
		return regexs;
	}
	
	private void seelAllDir()
	{		
		//扫描白名单
		seekWhiteOrBlack(BWList.TYPE_WHITE);
		
		//扫描媒体库  (del 不需要扫描媒体库了)
//		if (android.os.Build.VERSION.SDK_INT >= 11)
//		{
//			queryMediaDB();
//		}
		
		//搜索全部
		seekAll(false, false);
		
		//扫描灰名单
		seekWhiteOrBlack(BWList.TYPE_GARY);
	}
	
	private void seekAll(boolean addWhiteAndGray, boolean seekPicture)
	{
		Stack<File> allCard = getSource();
		
		//sdcard优先搜
		String sdcardPath = OfficeApp.getInstance().sdcardPath;
		if (sdcardPath != null)
		{			
			seekCurrentFolder(new File(sdcardPath), addWhiteAndGray, seekPicture);
		}
		
		//搜剩余的SD卡
		for(File folder : allCard)
    	{
			if (sdcardPath != null)
			{
				//因为有的路径带斜杠有的不带，做两次包含的对比
				if (folder.getAbsolutePath().contains(sdcardPath))
					continue;
				
				if (sdcardPath.contains(folder.getAbsolutePath()))
					continue;
			}
			
			seekCurrentFolder(folder, addWhiteAndGray, seekPicture);
        }
	}

	/*
	 * 同步改变了的文件和文件夹
	 */
	public void syncAllChangedFiles(HashMap<String, Long> dirMap) {		
		//已经删除掉的文件踢出列表
		DataCenter.getInstance().deleteUnExistScan();
		
		//根据时间变化同步目录
		Iterator<Entry<String, Long>> dirIt = dirMap.entrySet().iterator();
		int i = 0;
		while (dirIt.hasNext()) {
			sleepThread(i++);
			Entry<String, Long> entry = dirIt.next();
			String dirpath = entry.getKey();
			long lastModifyTime = entry.getValue();
			File dirFile = new File(dirpath);
			long newModifiedTime = dirFile.lastModified();
			if (!dirFile.exists()) {
				DataCenter.getInstance().removeFromCacheDirPool(dirpath);
				continue;
			} else if (newModifiedTime == lastModifyTime) {				
				continue; // 没有改变
			} else { //改变了
			    String[] fileNames = dirFile.list();
				if (null == fileNames)
					continue;
				int j = 0;				
				for (String childName : fileNames) {
					sleepThread(j++);
					if (childName.startsWith(".")) { // 不扫描隐藏
		                    continue;
		            }
					File child = new File(dirFile, childName);
					if (child.isDirectory()) {  //是目录
						if(null == dirMap.get(child.getPath())) {  //原来没有加过，新加的文件夹
						    Integer bwType = BWList.getBwMap().get(child.getAbsolutePath());
		                    if (bwType != null && bwType ==BWList.TYPE_BLACK) { //黑名单 跳过不扫
		                        continue;
		                    }
							seekCurrentFolder(child, true, false);
						} else {  
							//在列表中不管
						}
					} else { 
						insertFile(child, childName, false);
					}
				}
				DataCenter.getInstance().insertCacheDirPool(dirpath, newModifiedTime);
			}
		}
	}
	/**
	 * 搜索当前文件夹
	 * addWhiteAndGray: 是否添加白和灰名单进去，是添加，否则排除掉
	 * 注: 该方法中 黑白灰名单只过滤 currentFolder的子节点，对currentFolder本身如要过滤请在外部判断
	 */
	private void seekCurrentFolder(File currentFolder, boolean addWhiteAndGray, boolean seekPicture) {
		Stack<File> openCheckedDirList = new Stack<File>();
		openCheckedDirList.add(currentFolder);		

		int i = 0;
		while (openCheckedDirList.size() > 0) {
			sleepThread(i++);
			currentFolder = openCheckedDirList.pop();			
			//添加目录缓存
			DataCenter.getInstance().insertCacheDirPool(currentFolder.getPath(), currentFolder.lastModified());
			
			String[] fileNames = currentFolder.list();
			if (null == fileNames)
				continue;
			for (String childName : fileNames) {
			    if (childName.startsWith(".")) { // 不扫描隐藏
					continue;
				}
			    File child = new File(currentFolder, childName);
				if (child.isDirectory()) {
					Integer bwType = BWList.getBwMap().get(child.getAbsolutePath());
					if (bwType != null && bwType ==BWList.TYPE_BLACK) { //黑名单 跳过不扫
						continue;
					}
					
					if (addWhiteAndGray) {  //忽略黑灰名单 强制扫描
						openCheckedDirList.push(child);
					} else { //不忽略			
						if (bwType == null) {
							openCheckedDirList.push(child);
						}
					}
				} else { // if (changed)
					insertFile(child, childName, seekPicture);
				}
			}
		}
	}

	private void sleepThread(int i) {
		if ((i % 1000) == 0) {  //让出cpu时间片
			try {
				Thread.sleep(10);
			} catch (InterruptedException e) {
			}
		}
	}
			
    /**
     * 搜索白名单 
     */
	private void seekWhiteOrBlack(int isBlack)
    {
    	Iterator<String> iterator = BWList.getBwMap().keySet().iterator();
    	while(iterator.hasNext())
    	{
    		String childPath = iterator.next();
    		File currentFolder = new File(childPath);
    		if (!currentFolder.exists()) {
    			continue;
    		}
    		int chileBwList = BWList.getBwMap().get(childPath);
    		if (chileBwList == isBlack) {
    			seekCurrentFolder(currentFolder, true, false);
    		}
    	}
    }
	
	public void insertFile(File file, String fileName)
	{
		insertFile(file, fileName, false);
	}
			
   
    // 过滤文件类型.
	public void insertFile(File file, String fileName, boolean seekPicture)
	{
		String suffix = StringUtil.getFileNameSuffix(fileName);
		suffix = suffix.toLowerCase();
		for (int i = 0; i < regexs.size(); i++) 
		{
			int key = regexs.keyAt(i);
			Set<String> value = regexs.valueAt(i);
			if (value.contains(suffix))
			{
				DataCenter.getInstance().insertFile(file, key);
				break;
			}
		}
		
		if (seekPicture)
		{
			if (pictureTypes.contains(suffix))
			{
				insertPic(file.getAbsolutePath());
			}
		}
	}
	
	private void insertPic(String path)
	{
		try
		{
			MediaScannerConnection.scanFile(OfficeApp.getInstance(),  
					new String[] { path }, null,  null);  
		}
		catch (Exception e)
		{
		}
	}
	
	public static Stack<File> getSource() {
		Set<String> paths = new HashSet<String>();

		if (null != OfficeApp.getInstance().sdcardPath) {
			File f = new File(OfficeApp.getInstance().sdcardPath);

			// SD卡.
			if (f.exists()) {
				paths.add(OfficeApp.getInstance().sdcardPath);
			}
		}

		//TODO demo屏蔽
		// 其他外部存储.
//		if (!VersionManager.instance().isDisableExternalVolumes()) {
//			ArrayList<FileAttribute> tmpExtVolumes = FileBrowserOperatorCore.getExtVolumes(OfficeApp.getInstance());
//			
//			if(null != tmpExtVolumes){
//    			String extPath = null;
//    			for (FileAttribute d : tmpExtVolumes) {
//    				extPath = d.getPath();
//    				if (!paths.contains(extPath) && new File(extPath).exists() /*&& !FileBrowserOperatorCore.isWriteLimitedPath(extPath)*/) { //去掉检测可写状态，只要能读就扫描出来 bugid:196411
//    					paths.add(extPath);
//    				}
//    			}
//			}
//		}
		// APP 内部存储路径.
//		paths.add(OfficeApp.getInstance().currentDirectory);
		
		Stack<File> rootDirSet = new Stack<File>();
		
		File f = null;
		for(String p : paths)
		{
			f = new File(p);
			if (f.exists())
				rootDirSet.add(f);
		}
				
		return rootDirSet;
	}
	//=======end==
}
